/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief JPEG encoder interface using the libjpeg (from IJG)
 * \author Antti Maula <antti.maula@tkk.fi>
 * \author Jari Saarinen
 *
 * $Id: JPEGEncoder.hpp,v 1.11 2009-09-28 08:55:34 amaula Exp $
 *
 */
#ifndef _JPEGENCODER_HPP_
#define _JPEGENCODER_HPP_
#include <stdio.h>
extern "C"{
#include <jpeglib.h>

#include <setjmp.h>
}
/**
 * JPEG encoder interface using the libjpeg (from IJG)
 *
 * This is CJPEGEncoder version 1.0.
 * 
 *
 * Version 1.1 (?.?.2007)
 * - Support for encoding from RGB and Grayscale added by Jari.
 *
 * Version 1.0 (9.7.2007)
 * - Initial implementation of the JPEGEncoder interface. Provides
 *   basic facilities to .. surprisingly enough .. Encode RGB images
 *   to JPEG.
 *
 */
class CJPEGEncoder 
{
  
  /**
   * Datatype for own data destination
   */
  typedef struct {
    struct jpeg_destination_mgr pub; /* public fields */
    FILE * outfile;		/* target stream */
    JOCTET * buffer;		/* start of buffer */
  } jdest_t;

public:


  /**
   * Default, Empty constructor.
   */
  CJPEGEncoder();


  /**
   * Default constructor. If called with parameters, the assigned
   * pointer is assigned as YUV source. See 'SetYUVDataPtr' for more
   * info.
   * 
   * @param[in] aData           Pointer to YUV data to Encode.
   * @param[in] aWidthPixels    Image width of YUV data (in pixels)
   * @param[in] aHeightPixels   Image height of YUV data (in pixels)
   */
  CJPEGEncoder(const char *aData,
	       const int aWidthPixels, const int aHeightPixels);


  /**
   * Default destructor.
   */
  ~CJPEGEncoder();
  

  /**
   * Assigns a pointer to read data from. NOTE: This only stores the
   * datasource pointer. Therefore, you must NOT destroy or modify the
   * provided pointer while using the instance. \See ReleaseYUVDataPtr
   * for more info. 
   * The YUV data is expected to be in YUV420P format.
   *
   
   *
   * The SetYUVDataPtr can be called again right after the user has
   * read all the data needed. This means that
   * the CJPEGEncoder class is reusable, and doesn't have to be reallocated
   * every time new image is processed. (In fact its recommended to NOT
   * construct a new CJPEGEncoder object for every new image, because the 
   * internal JPEG buffer
   * is preserved across calls, saving the time used for dynamic allocation)
   *
   * @param[in] aData           Pointer to YUV data to decode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   * @param[in] aWidthPixels    Image width of YUV data (in pixels)
   * @param[in] aHeightPixels   Image height of YUV data (in pixels)
   * @return                    true == success, false == failure. Note that
   *                            no analysis is done on the data itself
   *                            during this call.
   *
   */
  bool SetYUVDataPtr(const char *aData,
		     const int aWidthPixels, const int aHeightPixels);

  /** \overload bool SetYUV420PDataPtr(const char *aData,
   *  const int aWidthPixels, const int aHeightPixels)
   */
  bool SetYUV420PDataPtr(const char *aData,
                         const int aWidthPixels, const int aHeightPixels) {
    return SetYUVDataPtr(aData, aWidthPixels, aHeightPixels);
  }
  /**
   * See SetYUVDataPtr
   * Format:
   * * (<red><green><blue><red><green>...) All single color components are 8 bits
   */
  bool SetRGBDataPtr(const char *aData,
		     const int aWidthPixels, const int aHeightPixels);
  /**
   * Compression with greyscale image
   * See SetYUVDataPtr.
   * The data is given in one dimentional char array sized 1*W*H 
   **/
  bool SetGrayscaleDataPtr(const char *aData,
			   const int aWidthPixels, const int aHeightPixels);
  /**
   * If you really need to release the YUV data pointer, but need to
   * retain the YUV data, this function is for you. This function takes
   * care of decoding the image (if not already done so) and releases
   * the YUV data pointer after that. After call to this, it is safe
   * to modify/delete the previously assigned YUV data pointer.
   *
   * @return                    true == success. The image was succesfully
   *                            encoded, and the JPEG format data is now
   *                            available.
   *                            false == failure. Image decoding failed.
   *                            The JPEG data pointer is released as described,
   *                            but the JPEG data is not available.
   */ 
  bool ReleaseYUVDataPtr(void);


  /**
   * This function returns a pointer to beginning of JPEG data. 
   * The pointer points to internal buffer, so modifying the returned buffer
   * is not allowed. Also, while using the data the instance must be kept
   * alive. Also, calling the SetJPEGDataPtr invalidates the pointer.
   * 
   * @return                    Pointer to JPEG data or NULL on failure.
   *
   */
  const char *GetJPEGDataPtr(void);


  /**
   * Works just like \see GetJPEGDataPtr, except that the data is copied to
   * separate user-supplied buffer. This avoids buffer invalidation in case of
   * changing ptr.
   * 
   * @param[in] aDataPtr        Pointer to memory location to copy the JPEG data.
   * @param[in] aMaxBufSize     Buffer size available. (May not be used completely,
   *                            but atleast the number of bytes returned by
   *                            GetYUVDataSize(void) is required)
   * @return                    true == success. Image Compress was succesfull,
   *                            and the data is now stored to user supplied pointer.
   *                            false == failure. Image compression failed, or 
   *                            user supplied buffer was too small.
   *
   */ 
  bool GetJPEGDataCopy(char *aDataPtr, const int aMaxBufSize);


  /**
   * Returns the number of bytes required by JPEG data
   * 
   * @return                    Positive number of bytes if encoding was succesfull, 
   *                            otherwise -1 on error.
   *  
   */
  int GetJPEGDataSize(void);


  /**
   * Set compression quality.
   * @TODO What does this parameter do?
   * 
   * @param[in] aQuality        Sets the JPEG compression quality to 'aQuality'
   *
   */
  bool SetJPEGQuality(const int aQuality);



public:
  /**
   * Backwards compatible function for compressing JPEG images.
   */
  size_t jpg_compress(unsigned char *image, int width, int height);
  unsigned char *data;
  unsigned int datacount;


private:
  // Initialize the JPEG object with defaults
  void InitializeJPEGLib(void);
  /**
   * Added by Jari
   * The mode tells which copressor to use
   * Currently implemented:
   * JCS_YCbCr = YUV420P 
   * JCS_RGB  = RGB
   * JCS_GRAYSCALE = Grayscale
   */
  unsigned int mode;
	
  // Encoded JPEG data here.
  char *iJPEGData;
  int iJPEGDataSize;
  
  
  // JPEG Handling data
  struct jpeg_compress_struct iJpegEnc;
  jdest_t iJPEGBuffer;
  struct jpeg_error_mgr iJpegErr;
  jmp_buf iEncoderStartJumpPoint;


  // YUV pointers
  unsigned char *iYUVRawDataPtr;
  unsigned int iYUVRawDataSize;


  // Status flags
  bool iGotJPEG;
  const char *CompressYUV420P(void);
  const char *CompressRGB(void);
  /**
   * Compresses a grayscale images
   * FIXME: Not tested!
   */
  const char *CompressGreyscale(void);
  
  // JPEG Library required static callbacks.
  static boolean jpegencoder_empty_output_buffer (j_compress_ptr cinfo);
  static void jpegencoder_term_destination (j_compress_ptr cinfo);
  static void jpegencoder_init_destination (j_compress_ptr cinfo);
  static void jpegencoder_error_exit (j_common_ptr cinfo);
  static void jpegencoder_output_message (j_common_ptr cinfo);
  static void jpegencoder_format_message (j_common_ptr cinfo, char * buffer);
  static void jpegencoder_emit_message (j_common_ptr cinfo, int msg_level);


  // Private constructors (Copying of JPEGEncoder object denied)
  CJPEGEncoder(const CJPEGEncoder &)
    : data(NULL),
      datacount(0),
      mode(0),
      iJPEGData(NULL),
      iJPEGDataSize(0),
      iJpegEnc(),
      iJPEGBuffer(),
      iJpegErr(),
      iEncoderStartJumpPoint(),
      iYUVRawDataPtr(NULL),
      iYUVRawDataSize(0),
      iGotJPEG(false) {}
  CJPEGEncoder &operator =(const CJPEGEncoder &) { return *this; }
};

/*****************************************************************************\
 *
 * $Log: not supported by cvs2svn $
 * Revision 1.10  2008-10-17 09:03:00  amaula
 * Added type-aware wrapper function.
 *
 * Revision 1.9  2008-08-12 13:07:19  amaula
 * Typofix on doc.
 *
 * Revision 1.8  2008-07-08 12:16:02  mmatusia
 * Added extern C over jpeglib.h (not linking properly on arm)
 *
 * Revision 1.7  2008-01-04 11:30:51  amaula
 * Code cleanups.
 *
 * Revision 1.6  2008-01-03 10:23:15  jari
 * alustava tuki grayscalelle
 *
 * Revision 1.5  2007-12-14 14:18:13  jari
 * copy-paste-korjaus
 *
 * Revision 1.4  2007-12-14 12:13:26  jari
 * JPEG Compression support for RGB data added
 *
 * Revision 1.3  2007-11-28 11:14:52  amaula
 * Small bugfixes
 *
 * Revision 1.2  2007-11-28 08:47:08  amaula
 * Now it compiles :)
 *
 * Revision 1.1  2007-07-13 07:25:13  amaula
 * Initial import; Base structure crafted - Not working yet.
 *
 *
 *
\*****************************************************************************/

#endif
